/**
 * @(#)TaskLuaManager.java 2017年4月18日
 * Copyright(C) 2017 ZHEXIN IT CO.,LTD. All rights reserved.
 */
package org.jsbd.boss.service.impl;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.time.DateFormatUtils;
import org.jsbd.boss.common.redis.RedisClient;
import org.jsbd.boss.util.CacheKey;
import org.jsbd.boss.util.DateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.connection.ReturnType;
import org.springframework.stereotype.Service;

/**
 * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
 * @version 1.0.0
 */
@Service
public class TaskLuaManager {
    public static final Logger LOGGER = LoggerFactory.getLogger(TaskLuaManager.class);

    @Resource
    private RedisClient redisClient;

    @Resource
    private RedisClient redisClientTemp;


    /**
     * getTaskOfMaxSize:根据最大任务限制取出任务
     *
     * @return 返回2表示任务没有，返回1表示任务已执行完，返回string任务表示正常返回一个任务
     * @throws UnsupportedEncodingException
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public String getTaskOfMaxSize(String queueKey) throws UnsupportedEncodingException {
        try {
            String sendKey = CacheKey.SEND_TASK_RECORD_TIME + DateFormatUtils.format(new Date(), "yyyyMMddHHmmss");
            String maxSizeKey = CacheKey.SEND_TASK_MAX_SIZE;
            StringBuffer sb = new StringBuffer();
            sb.append("local secondValue=redis.call('incrby',KEYS[1],0) redis.call('expire',KEYS[1],120) local maxSize=redis.call('get',KEYS[2])");
            sb.append(" if secondValue and maxSize and tonumber(secondValue)<tonumber(maxSize)");
            sb.append(" then local task=redis.call('rPop', KEYS[3])");
            sb.append(" if task");
            sb.append(" then redis.call('incr',KEYS[1])");
            sb.append(" return task");
            sb.append(" else return 2 end else return 1 end");
            Object task = redisClient.eval(sb.toString(), ReturnType.VALUE, new String[]{sendKey, maxSizeKey, queueKey});
            if (task != null) {
                if (task instanceof Long) {
                    return task.toString();
                } else {
                    return new String((byte[]) task, "UTF-8");
                }
            }
        } catch (Exception e) {
            LOGGER.error("获取任务失败", e);

        }
        return null;
    }

    /**
     * getQueueSize:获取任务队列大小
     *
     * @param keys
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public Integer[] getQueueSize(String[] keys) {
        Integer[] sizes = null;
        if (keys != null && keys.length > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("local result={}");
            sb.append(" for i=1,#KEYS");
            sb.append(" do");
            sb.append(" if(redis.call('exists',KEYS[i])==1)");
            sb.append(" then");
            sb.append(" result[i]=redis.call('llen', KEYS[i])");
            sb.append(" else");
            sb.append(" result[i]=0");
            sb.append(" end");
            sb.append(" end ");
            sb.append(" return result");
            Object retult = redisClient.eval(sb.toString(), ReturnType.MULTI, keys);
            if (retult != null) {
                List<Long> numList = (List) retult;
                sizes = new Integer[numList.size()];
                for (int i = 0; i < numList.size(); i++) {
                    Long currNum = (Long) numList.get(i);
                    sizes[i] = currNum == null ? 0 : currNum.intValue();
                }
            }
        }
        return sizes;
    }


    /**
     * getQueueSize:获取任务队列大小
     *
     * @param keys
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public Integer[] getTempQueueSize(String[] keys) {
        Integer[] sizes = null;
        if (keys != null && keys.length > 0) {
            StringBuilder sb = new StringBuilder();
            sb.append("local result={}");
            sb.append(" for i=1,#KEYS");
            sb.append(" do");
            sb.append(" if(redis.call('exists',KEYS[i])==1)");
            sb.append(" then");
            sb.append(" result[i]=redis.call('llen', KEYS[i])");
            sb.append(" else");
            sb.append(" result[i]=0");
            sb.append(" end");
            sb.append(" end ");
            sb.append(" return result");
            Object retult = redisClientTemp.eval(sb.toString(), ReturnType.MULTI, keys);
            if (retult != null) {
                List<Long> numList = (List) retult;
                sizes = new Integer[numList.size()];
                for (int i = 0; i < numList.size(); i++) {
                    Long currNum = (Long) numList.get(i);
                    sizes[i] = currNum == null ? 0 : currNum.intValue();
                }
            }
        }
        return sizes;
    }


    /**
     * getLeftTaskNumLua:剩余任务量
     *
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    private String getLeftTaskNumLua() {
        StringBuilder sb = new StringBuilder();
        sb.append("local result={}");
        sb.append(" for i = 1,#KEYS do redis.call('hincrby', KEYS[i], 'total',0) redis.call('hincrby', KEYS[i], 'exectotal',0)");
        sb.append(" result[i]= redis.call('hget', KEYS[i], 'total')-redis.call('hget', KEYS[i], 'exectotal') end return result");
        return sb.toString();
    }

    private String initTask() {
        StringBuilder sb = new StringBuilder();
        sb.append("for i=2,#KEYS,2");
        sb.append(" do");
        sb.append(" redis.call('hset', KEYS[i], 'total', KEYS[i+1])");
        sb.append(" redis.call('hincrby', KEYS[i], 'exectotal', 0) ");
        sb.append(" redis.call('expire', KEYS[i], KEYS[1]) ");
        sb.append(" end");
        sb.append(" return true");
        return sb.toString();
    }

    /**
     * getLeftTaskNum:任务可用量
     *
     * @param taskKeys
     * @return
     * @throws Exception
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public List<Integer> getLeftTaskNum(List<String> taskKeys) throws Exception {
        List<Integer> sizeList = new ArrayList<Integer>();
        if (taskKeys != null && taskKeys.size() > 0) {
            Object retult = redisClient.eval(getLeftTaskNumLua(), ReturnType.MULTI, taskKeys);
            if (retult != null) {
                List<Long> numList = (List) retult;
                for (int i = 0; i < numList.size(); i++) {
                    Long currNum = numList.get(i);
                    sizeList.add(currNum == null ? 0 : currNum.intValue());
                }
            }
        }
        return sizeList;
    }


    /**
     * getNewLeftTaskNum:新增任务余量
     *
     * @param apkList
     * @return
     * @throws Exception
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public List<Integer> getNewLeftTaskNum(List<String> apkList) throws Exception {
        List<String> taskKeys = new ArrayList<>();
        if (apkList != null) {
            for (String apk : apkList) {
                taskKeys.add(initApkTaskCacheKey(apk));
            }
        }
        return getLeftTaskNum(taskKeys);
    }

    private String initApkTaskCacheKey(String mark) {
        return CacheKey.A_TASK + DateUtil.getDaysOfTime() + mark;
    }


    public Boolean addTempQueue(String key, List<String> list) {
        String leftTime = String.valueOf(DateUtil.leftTimeToMidNight() / 1000);
        list.add(0, key);
        list.add(1, leftTime);
        String lua = "for i = 3,#(KEYS) do redis.call('lpush',KEYS[1], KEYS[i]) end redis.call('expire',KEYS[1],KEYS[2])  return true";
        return (Boolean) redisClientTemp.eval(lua, ReturnType.BOOLEAN, list.toArray(new String[0]));
    }


    /**
     * updateTaskRecord:任务记录
     *
     * @param keysAndRecords
     */
    public void updateTaskRecord(List<String> keysAndRecords) {
        List<String> keyAndArgs = new ArrayList<String>();
        String leftTime = String.valueOf(DateUtil.leftTimeToMidNight() / 1000);
        keyAndArgs.add(leftTime);
        for (int i = 0; i < keysAndRecords.size(); i = i + 2) {
            keyAndArgs.add(keysAndRecords.get(i));
            keyAndArgs.add(keysAndRecords.get(i + 1));
        }
        redisClient.eval(initTask(), ReturnType.BOOLEAN, keyAndArgs.toArray(new String[0]));
    }


    /**
     * getApkNumOfDay:获取天次数的可执行量 总量-已执行量
     *
     * @param apk
     * @param day
     * @return
     * @author <a href="mailto:xupengcheng@zhexinit.com" >徐鹏程</a>
     */
    public int getLeftNumOfDayTimes(String apk, Integer day, Integer execTimes, long dayOfTime) {
        try {
            String lua = "return tonumber(redis.call('hget',KEYS[1],KEYS[2]))-tonumber(redis.call('hget',KEYS[1],KEYS[3]))";
            Object obj = redisClient.eval(lua, ReturnType.VALUE, new String[]{CacheKey.AR_DAY_HASH_KEY + dayOfTime + apk, day + ":" + execTimes + ":" + "total", day + ":" + execTimes + ":" + "exectotal"});
            if (obj != null) {
                return ((Long) obj).intValue();
            }
        } catch (Exception e) {
            redisClient.evalHincrby(CacheKey.AR_DAY_HASH_KEY + dayOfTime + apk, day + ":" + execTimes + ":" + "total", 0);
            redisClient.evalHincrby(CacheKey.AR_DAY_HASH_KEY + dayOfTime + apk, day + ":" + execTimes + ":" + "exectotal", 0);
//            LOGGER.error("getApkNumOfDay apk {}  day {}  error", new Object[]{apk, day, e});
        }
        return 0;
    }


}
